/*
 * MainPanel.java
 *
 * Created on 23 de Janeiro de 2010, 15:17
 */

package gui;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Line2D.Double;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Iterator;
import javax.swing.event.MouseInputListener;
import model.GrafoVisibilidade;

/**
 *
 * @author  leonardoff
 */
public abstract class MainPanel extends javax.swing.JPanel implements MouseInputListener {
    
    private final int NXT_WIDTH       = 152;
    private final int NXT_HEIGHT      = 136;
    private Point CENTRO_RODAS  ;//= new Point(124, 68);
    private final int DISTANCIA_RODAS = 110;
    
    private final float proporcao     = 0.5f;
    private double diagonal = (Math.hypot(NXT_WIDTH, NXT_HEIGHT) / 2);

    private ArrayList<Polygon> obstaculos = new ArrayList<Polygon>();
    private Point2D pontoEscolhido;
    private GrafoVisibilidade grafo;
    private ArrayList<Line2D.Double> linhasGrafo;
    int auxiliarMouse = 0;
    
    
    public MainPanel() {
        initComponents();
        this.addMouseListener(this);

        int xpoints[] = {400,500,500,400};
        int ypoints[] = {450,450,550,550};
        obstaculos.add(new Polygon(xpoints, ypoints, xpoints.length));
    }
    
    abstract void movimentarRobo(double x, double y);
    abstract void mouseClicado();
        
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        setBackground(new java.awt.Color(255, 255, 204));
        setPreferredSize(new java.awt.Dimension(500, 500));
        setLayout(new java.awt.BorderLayout());
    }// </editor-fold>//GEN-END:initComponents


    public void gerarGrafos() {

        System.out.println("Gerando grafo....");
        
        // 1
        ArrayList<Point2D> pontosExistentes = new ArrayList<Point2D>();
        pontosExistentes.add(CENTRO_RODAS);
        for (Polygon polygon : obstaculos)
            for(int i = 0; i < polygon.npoints; i++)
                pontosExistentes.add(new Point(polygon.xpoints[i], polygon.ypoints[i]));
        pontosExistentes.add(pontoEscolhido);
        grafo = new GrafoVisibilidade(pontosExistentes);
        System.out.println("  Pontos Existentes: " + pontosExistentes.size());
        
        // 2
        ArrayList<Line2D.Double> linhasObstaculos = new ArrayList<Line2D.Double>();
        for (Polygon polygon : obstaculos) {
            for(int i = 0; i < polygon.npoints-1; i++) {
                Line2D.Double linha = new Line2D.Double(polygon.xpoints[i], polygon.ypoints[i],
                                                            polygon.xpoints[i+1], polygon.ypoints[i+1]);
                linhasObstaculos.add(linha);
            }
            Line2D.Double linha = new Line2D.Double(polygon.xpoints[polygon.npoints-1], polygon.ypoints[polygon.npoints-1],
                                                            polygon.xpoints[0], polygon.ypoints[0]);
            linhasObstaculos.add(linha);
        }
        System.out.println("  Linhas de Obstáculos: " + linhasObstaculos.size());
        
        // 3
        linhasGrafo = new ArrayList<Line2D.Double>();
        Iterator<Double> it = linhasObstaculos.iterator();
        boolean intersecta = false;
        int contLinhas = 0;

        for (int i = 0; i < pontosExistentes.size(); i++) {
            Point2D pontoAtual = pontosExistentes.get(i);
            for (int j = i+1; j < pontosExistentes.size(); j++) {
                Line2D.Double linhaAtual = new Line2D.Double(pontoAtual, pontosExistentes.get(j));

                if(isEixoObstaculo(it, linhaAtual)) {
                    grafo.addArco(i, j);
                    contLinhas++;
                }
                else {
                    for (Double linhaObstaculo : linhasObstaculos) {
                        if(intersecta(linhaAtual, linhaObstaculo)) {
                            intersecta = true;
                            //break;
                        }
                    }
                    /*if(!intersecta) { grafo[i][j] = 1; contLinhas++; }
                    intersecta = false;*/
                    if(!intersecta) linhasGrafo.add(linhaAtual);
                    intersecta = false;
                }
            }
        }
        System.out.println("  Linhas do Grafo: " + contLinhas);


        // 4
        for (Polygon polygon : obstaculos) {
            for (int i = 0; i < linhasGrafo.size(); i++) {
                if(polygon.contains(linhasGrafo.get(i).getBounds2D())) 
                    linhasGrafo.remove(i);                
            }
        }
        System.out.println("  Novas Linhas do Grafo: " + linhasGrafo.size());


        System.out.println("Grafo Gerado!");
        repaint();
    }

    private boolean isEixoObstaculo(Iterator<Double> it, Line2D linha) {
        while(it.hasNext()) {
            Line2D.Double l = it.next();
            if((l.getP1().distance(linha.getP1()) == 0 && l.getP2().distance(linha.getP2()) == 0) ||
                    (l.getP1().distance(linha.getP2()) == 0 && l.getP2().distance(linha.getP1()) == 0))
                return true;
        }
        return false;
    }

    private boolean intersecta(Line2D.Double linha1, Line2D.Double linha2) {
        if(!linha1.intersectsLine(linha2))
            return false;
        else {
            if(linha1.getP1().distance(linha2.getP1()) == 0 || linha1.getP1().distance(linha2.getP2()) == 0 ||
                    linha1.getP2().distance(linha2.getP1()) == 0 || linha1.getP2().distance(linha2.getP2()) == 0)
                return false;
            else
                return true;
        }
    }

    
    @Override
    public void paint(Graphics g) {
        super.paint(g);

        // Desenha grade
        float distDiv = 50 * proporcao;
        int numX = (int) Math.floor(this.getWidth() / distDiv);
        int numY = (int) Math.floor(this.getHeight() / distDiv);
        
        g.setColor(Color.LIGHT_GRAY);

        for(int i = 1; i <= numX; i++)
            g.drawLine((int)(i * distDiv), 0, (int)(i * distDiv), this.getHeight());
        for(int i = 1; i <= numY; i++)
            g.drawLine(0, (int)(i * distDiv), this.getWidth(), (int)(i * distDiv));


        // Desenha robô
        g.setColor(Color.BLACK);
        g.drawRect(0, 0, (int)(NXT_WIDTH * proporcao), (int)(NXT_HEIGHT * proporcao));
        g.setColor(new Color(255, 0, 0, 160));
        g.fillRect(1, 1, (int)(NXT_WIDTH * proporcao), (int)(NXT_HEIGHT * proporcao));


        // Desenha grafo
        if(linhasGrafo != null) {
            g.setColor(Color.BLUE);
            for (Line2D.Double linha : linhasGrafo) {
                g.drawLine((int)(linha.getX1() * proporcao), (int)(linha.getY1() * proporcao),
                        (int)(linha.getX2() * proporcao), (int)(linha.getY2() * proporcao));
            }
        }


        // Desenha centro das rodas
        if(CENTRO_RODAS != null){
        g.setColor(Color.ORANGE);
        g.drawOval((int)((CENTRO_RODAS.x * proporcao) - 3), (int)((CENTRO_RODAS.y * proporcao) - 3), 7, 7);
        g.setColor(Color.YELLOW);
        g.fillOval((int)((CENTRO_RODAS.x * proporcao) - 3), (int)((CENTRO_RODAS.y * proporcao) - 3), 7, 7);

        }
        // Desenha obstaculos
        for(int i = 0; i < obstaculos.size(); i++) {
            g.setColor(Color.BLACK);
            g.fillPolygon(adequarPoligono(obstaculos.get(i)));

            for(int j = 0; j < obstaculos.get(i).npoints; j++) {
                g.setColor(Color.BLACK);
                g.drawOval((int)(obstaculos.get(i).xpoints[j] * proporcao) - 3, (int)(obstaculos.get(i).ypoints[j] * proporcao) - 3, 7, 7);
                g.setColor(Color.WHITE);
                g.fillOval((int)(obstaculos.get(i).xpoints[j] * proporcao) - 3, (int)(obstaculos.get(i).ypoints[j] * proporcao) - 3, 7, 7);
            }
        }


        // Desenha ponto final
        if(pontoEscolhido != null) {

            g.setColor(Color.ORANGE);
            g.drawOval((int)(pontoEscolhido.getX() * proporcao) - 3, (int)(pontoEscolhido.getY() * proporcao) - 3, 7, 7);
            if(auxiliarMouse==0)
            {
                g.setColor(Color.YELLOW);        auxiliarMouse ++;
            }
            else g.setColor(Color.RED);
            g.fillOval((int)(pontoEscolhido.getX() * proporcao) - 3, (int)(pontoEscolhido.getY() * proporcao) - 3, 7, 7);
        }
    }

    public Polygon adequarPoligono(Polygon p) {
        
        int[] xpoints = new int[p.npoints], ypoints = new int[p.npoints];

        for (int i = 0; i < p.npoints; i++) {
            xpoints[i] = (int)(p.xpoints[i] * proporcao);
            ypoints[i] = (int)(p.ypoints[i] * proporcao);
        }

        return new Polygon(xpoints, ypoints, p.npoints);
    }

    public void mouseClicked(MouseEvent e) {

        //Essa condicao foi feita para selecao do ponto inicial
        if(auxiliarMouse==0){
        pontoEscolhido = e.getPoint();
        CENTRO_RODAS = (Point) pontoEscolhido;
        }
        else pontoEscolhido = new Point2D.Double(e.getPoint().x / proporcao, e.getPoint().y / proporcao);
        System.out.println(" > PONTO NA TELA :  [" + e.getPoint().x + ", " + e.getPoint().y + "]");
        System.out.println(" > PONTO REAL    :  [" + pontoEscolhido.getX() + ", " + pontoEscolhido.getY() + "]");
        System.out.println(" ");

        linhasGrafo = null;
        repaint();

        double xFinalReal = pontoEscolhido.getX() - CENTRO_RODAS.x;
        double yFinalReal = pontoEscolhido.getY() - CENTRO_RODAS.y;
        
        System.out.println(" > INÍCIO   : [" + CENTRO_RODAS.x + ", " + CENTRO_RODAS.y + "]");
        System.out.println(" > DESTINO  : [" + xFinalReal + ", " + yFinalReal + "]");
        System.out.println(" ---------------------------- \n");

        movimentarRobo(xFinalReal, yFinalReal);
        mouseClicado();
    }

    public void PontoInicial(){
        CENTRO_RODAS = new Point();
        auxiliarMouse =0;
    }

    public void mousePressed(MouseEvent e) {
        
    }

    public void mouseReleased(MouseEvent e) {
        
    }

    public void mouseEntered(MouseEvent e) {
        
    }

    public void mouseExited(MouseEvent e) {
        
    }

    public void mouseDragged(MouseEvent e) {
        
    }

    public void mouseMoved(MouseEvent e) {
        
    }   
    
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables
    
}
